package com.dawn.framework.common.util.http;

import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * @description:
 * @author: cnsu-cmh
 * @time: 2024/8/7 13:07
 */
public class RestTemplateUtils {
    private static final Logger log = LoggerFactory.getLogger(RestTemplateUtils.class);

    // 时间，重试次数。
    private final static int CONNECT_TIMEOUT = 180000;
    private final static int READ_TIMEOUT   = 180000;
    private final static int RETRY_COUNT    = 1;


    /**
     * https 请求 GET
     * @param url           地址
     * @param connectTimeout 连接时间  毫秒
     * @param readTimeout   读取时间  毫秒
     * @param retryCount    重试机制
     * @return String 类型
     */
    public static String getHttp(String url, int connectTimeout, int readTimeout, int retryCount) {
        RestTemplate restTemplate = simpleClient(url, connectTimeout, readTimeout);
        String result = null; // 返回值类型;
        for (int i = 1; i <= retryCount; i++) {
            try {
                result = restTemplate.getForObject(url, String.class);
                return result;
            } catch (RestClientException e) {
                log.error("-----------开始-----------重试count: " + i);
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * https 请求 GET
     * @param url           地址
     * @return String 类型
     */
    public static String getHttp(String url) {
        RestTemplate restTemplate = simpleClient(url, CONNECT_TIMEOUT, READ_TIMEOUT);
        String result = null; // 返回值类型;
        for (int i = 1; i <= RETRY_COUNT; i++) {
            try {
                result = restTemplate.getForObject(url, String.class);
                return result;
            } catch (RestClientException e) {
                log.error("-----------开始-----------重试count: " + i);
                e.printStackTrace();
            }
        }
        return null;
    }


    /**
     * 加密参数类型请求
     * http 请求 post
     * @param url 地址
     * @return String 类型
     */
    public static String postHttp(String url) {
        RestTemplate restTemplate = simpleClient(url, CONNECT_TIMEOUT, READ_TIMEOUT);
        // 设置·header信息
        HttpHeaders requestHeaders = new HttpHeaders();
        Map headersMap = new HashMap();
        headersMap.put("Content-Type", "application/json;charset=utf-8");
        requestHeaders.setAll(headersMap);
        HttpEntity<JSONObject> requestEntity = new HttpEntity<JSONObject>(null, requestHeaders); // josn utf-8 格式
        String result = null; // 返回值类型;
        for (int i = 1; i <= RETRY_COUNT; i++) {
            try {
                result = restTemplate.postForObject(url, requestEntity, String.class);
                return result;
            } catch (RestClientException e) {
                log.error("-----------开始-----------重试count: " + i);
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * http 请求 post
     * @param url           地址
     * @param params        参数
     * @param headersMap    header
     * @param connectTimeout 连接时间
     * @param readTimeout   读取时间`
     * @param retryCount    重试机制
     * @return String 类型
     */
    public static String postHttp(String url, JSONObject params, Map headersMap, int connectTimeout, int readTimeout, int retryCount) {
        RestTemplate restTemplate =  simpleClient(url, connectTimeout, readTimeout);
        // 设置·header信息
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setAll(headersMap);
        HttpEntity<JSONObject> requestEntity = new HttpEntity<JSONObject>(params, requestHeaders); // josn utf-8 格式
        String result = null; // 返回值类型;
        for (int i = 1; i <= retryCount; i++) {
            try {
                result = restTemplate.postForObject(url, requestEntity, String.class);
                return result;
            } catch (RestClientException e) {
                log.error("-----------开始-----------重试count: " + i);
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * http 请求 post
     * @param url           地址
     * @param params        参数
     * @param headersMap    header
     * @return String 类型
     */
    public static String postHttp(String url, JSONObject params, Map headersMap) {
        RestTemplate restTemplate = simpleClient(url, CONNECT_TIMEOUT, READ_TIMEOUT);
        // 设置·header信息
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setAll(headersMap);
        HttpEntity<JSONObject> requestEntity = new HttpEntity<JSONObject>(params, requestHeaders); // josn utf-8 格式
        String result = null; // 返回值类型;
        for (int i = 1; i <= RETRY_COUNT; i++) {
            try {
                result = restTemplate.postForObject(url, requestEntity, String.class);
                return result;
            } catch (RestClientException e) {
                log.error("-----------开始-----------重试count: " + i);
                e.printStackTrace();
            }
        }
        return null;
    }

    public static String postHttp(String url, String jsonStr, Map headersMap) {
        RestTemplate restTemplate = simpleClient(url, CONNECT_TIMEOUT, READ_TIMEOUT);
        // 设置·header信息
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setAll(headersMap);
        HttpEntity<String> requestEntity = new HttpEntity<String>(jsonStr, requestHeaders); // josn utf-8 格式
        String result = null; // 返回值类型;
        for (int i = 1; i <= RETRY_COUNT; i++) {
            try {
                result = restTemplate.postForObject(url, requestEntity, String.class);
                return result;
            } catch (RestClientException e) {
                log.error("-----------开始-----------重试count: " + i);
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 加密参数类型请求  application/x-www-form-urlencoded
     * MultiValueMap<String, Object>
     * 采用 HttpEntity<MultiValueMap<String, Object>> 构造
     * http 请求 post
     *
     * @param url           地址
     * @param postParameters 参数
     * @param headersMap    header
     * @param connectTimeout 连接时间
     * @param readTimeout   读取时间
     * @param retryCount    重试机制
     * @return String 类型
     */
    public static String postHttpEncryption(String url, MultiValueMap<String, Object> postParameters, Map headersMap, int connectTimeout, int readTimeout, int retryCount) {
        RestTemplate restTemplate = simpleClient(url, connectTimeout, readTimeout);
        // 设置·header信息
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setAll(headersMap);
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(postParameters, requestHeaders);
        String result = null; // 返回值类型;
        for (int i = 1; i <= retryCount; i++) {
            try {
                result = restTemplate.postForObject(url, requestEntity, String.class);
                return result;
            } catch (RestClientException e) {
                log.error("-----------开始-----------重试count: " + i);
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 加密参数类型请求  application/x-www-form-urlencoded
     * MultiValueMap<String, Object>
     * 采用 HttpEntity<MultiValueMap<String, Object>> 构造
     * http 请求 post
     * @param url           地址
     * @param postParameters 参数
     * @param headersMap    header
     * @return String 类型
     */
    public static String postHttpEncryption(String url, MultiValueMap<String, Object> postParameters, Map headersMap) {
        RestTemplate restTemplate = simpleClient(url, CONNECT_TIMEOUT, READ_TIMEOUT);
        // 设置·header信息
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setAll(headersMap);
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(postParameters, requestHeaders);
        String result = null; // 返回值类型;
        for (int i = 1; i <= RETRY_COUNT; i++) {
            try {
                result = restTemplate.postForObject(url, requestEntity, String.class);
                return result;
            } catch (RestClientException e) {
                log.error("-----------开始-----------重试count: " + i);
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 加密参数类型请求  application/x-www-form-urlencoded
     * Map<String, Object>
     * 采用 HttpEntity<MultiValueMap<String, Object>> 构造
     * http 请求 post
     * @param url           地址
     * @param parameters 参数
     * @return String 类型
     */
    public static String postForm(String url, Map<String, Object> parameters) {
        Map headersMap = new HashMap();
        headersMap.put("Content-Type", "application/x-www-form-urlencoded");
        MultiValueMap<String, Object> requestBody = new LinkedMultiValueMap<>();
        if(!CollectionUtils.isEmpty(parameters)){
            parameters.forEach((k,v) -> {
                requestBody.add(k,v);
            });

        }
        return postHttpEncryption(url,requestBody,headersMap);
    }

    /**
     * 加密参数类型请求  application/x-www-form-urlencoded
     * MultiValueMap<String, Object>
     * 采用 HttpEntity<MultiValueMap<String, Object>> 构造
     * http 请求 post
     * @param url           地址
     * @param postParameters 参数
     * @return String 类型
     */
    public static String postForm(String url, MultiValueMap<String, Object> postParameters) {
        Map headersMap = new HashMap();
        headersMap.put("Content-Type", "application/x-www-form-urlencoded");
        return postHttpEncryption(url,postParameters,headersMap);
    }

    /**
     * http 请求 get
     * @param url           地址
     * @param parameters 参数
     * @return String 类型
     */
    public static String getHttp(String url, Map<String, Object> parameters) {
        RestTemplate restTemplate = simpleClient(url, CONNECT_TIMEOUT, READ_TIMEOUT);
        String reallyUrl = url;
        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
        if(!parameters.isEmpty()){
            for (Map.Entry<String,Object> e:
                    parameters.entrySet()) {
                //构建查询参数
                builder.queryParam(e.getKey(),e.getValue());
            }
            //拼接好参数后的URl;
            reallyUrl = builder.build().toString();
        }
        String result = null; // 返回值类型;
        for (int i = 1; i <= RETRY_COUNT; i++) {
            try {
                result = restTemplate.getForObject(reallyUrl, String.class,parameters);
                return result;
            } catch (RestClientException e) {
                log.error("-----------开始-----------重试count: " + i);
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 加密参数类型请求
     * http 请求 post
     * @param url 地址
     * @param params 参数
     * @return String 类型
     */
    public static String postJson(String url,JSONObject params) {
        Map headersMap = new HashMap();
        headersMap.put("Content-Type", "application/json;charset=utf-8");
        return postHttp(url,params,headersMap);
    }


    /**
     * 加密参数类型请求
     * http 请求 post
     * @param url 地址
     * @param jsonStr  参数 json串
     * @return String 类型
     */
    public static String postJsonStr(String url,String jsonStr) {
        Map headersMap = new HashMap();
        headersMap.put("Content-Type", "application/json;charset=utf-8");
        return postHttp(url,jsonStr,headersMap);
    }

    /**
     * 加密参数类型请求
     * http 请求 post
     * @param url 地址
     * @param xml  参数
     * @return String 类型
     */
    public static String postXmlStr(String url,String xml) {
        Map headersMap = new HashMap();
        headersMap.put("Content-Type", "application/xml;charset=utf-8");
        return postHttp(url,xml,headersMap);
    }

    private static RestTemplate simpleClient(String url, int connectTimeout, int readTimeout) {
        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        requestFactory.setConnectTimeout(connectTimeout);
        requestFactory.setReadTimeout(readTimeout);
        RestTemplate restTemplate = new RestTemplate(requestFactory);
        restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8)); // 设置编码集
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler()); //error处理
        if (url.contains("https")){
            restTemplate.setRequestFactory(new HttpsClientRequestFactory()); // 绕过https
        }
        return restTemplate;
    }


    /**
     * @ClassName: DefaultResponseErrorHandler
     * @Description: TODO
     * @author:
     * @date: 2
     */
    private static class DefaultResponseErrorHandler implements ResponseErrorHandler {

        /**
         * 对response进行判断，如果是异常情况，返回true
         */
        @Override
        public boolean hasError(ClientHttpResponse response) throws IOException {
            return response.getStatusCode().value() != HttpServletResponse.SC_OK;
        }

        /**
         * 异常情况时的处理方法
         */
        @Override
        public void handleError(ClientHttpResponse response) throws IOException {
            BufferedReader br = new BufferedReader(new InputStreamReader(response.getBody()));
            StringBuilder sb = new StringBuilder();
            String str = null;
            while ((str = br.readLine()) != null) {
                sb.append(str);
            }
            try {
                throw new Exception(sb.toString());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

